package com.sxj.corejava.code13_JDBC;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

public class Test08_多表查询操作 {

    public static void main(String[] args) throws DataAccessException {
//        List<Emp> emps = selectAll();
//        for (Emp emp : emps) {
//            System.out.println(emp);
//        }
        Dept dept = selectById(1);
        System.out.println(dept);
    }

    /**
     * 多对一关系
     * 查询所有员工以及员工所属的部门信息
     * 员工id、name、salary,部门id、name、remark
     */
    public static List<Emp> selectAll() throws DataAccessException {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        List<Emp> emps = new ArrayList<>();
        try {
            conn = JDBCUtil.getConnection();
            String sql = new StringBuffer()
                    .append(" select e.id,e.name,e.salary,d.id 'dept_id',d.name 'dept_name',d.remark ")
                    .append(" from t_emp e ")
                    .append(" left join t_dept d ")
                    .append("     on e.dept_id = d.id ")
                    .toString();
            ps = conn.prepareStatement(sql);
            rs = ps.executeQuery();
            while (rs.next()) {
                // 此时的映射涉及到两个对象的数据
                // 因此,在实体类中对象关系维护时,所有外键字段的属性使用对象来表示
                // 1.Emp对象属性的映射
                Emp emp = new Emp();
                // 在进行多表查询时,会使用表名.列名表示查询指定表的指定列的数据
                // 但是生成的结果集中,其列名并不会使用[表名.列名],而是直接使用列名表示
                // 那么此时存在一个问题,如果多张表存在相同的字段会有什么后果?
                // 通过测试,我们发现,此时员工与部门相同字段的映射,所映射的数据始终是前面出现的字段的数据
                // 为了避免出现这个情况,我们可以为同名的字段赋予一个别名
                emp.setId(rs.getInt("id"));
                emp.setName(rs.getString("name"));
                emp.setSalary(rs.getDouble("salary"));
                // 2.Dept对象属性的映射
                Dept dept = new Dept();
                dept.setId(rs.getInt("dept_id"));
                dept.setName(rs.getString("dept_name"));
                dept.setRemark(rs.getString("remark"));

                // 由于dept对象属于Emp中的属性,因此映射完Dept对象之后,需要赋值给Emp中对应属性
                emp.setDept(dept);
                emps.add(emp);
            }
        } catch (DataAccessException e) {
            e.printStackTrace();
            throw e;
        } catch (SQLException e) {
            e.printStackTrace();
            throw new DataAccessException(e);
        } finally {
            JDBCUtil.close(conn, ps, rs);
        }
        return emps;
    }

    /**
     * 一对多
     * 根据编号查询指定部门以及该部门中的所有员工信息
     * @param id
     * @return
     */
    public static Dept selectById(Integer id) throws DataAccessException {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;
        Dept dept = null;
        try {
            conn = JDBCUtil.getConnection();
            String sql = new StringBuffer()
                    .append(" select d.id,d.name,d.remark,e.id 'e.id',e.name 'e.name',e.salary ")
                    .append(" from t_dept d ")
                    .append(" left join t_emp e ")
                    .append("     on d.id = e.dept_id ")
                    .append(" where d.id = ?")
                    .toString();
            ps = conn.prepareStatement(sql);
            ps.setInt(1, id);
            rs = ps.executeQuery();
            while (rs.next()) {
                // 此时的结果集中存在多行数据
                // 但是通过分析可以发现,有部分数据是完全一致的
                // 我们此时属于一对多映射
                // 一方的数据是完全一致的,多方的数据是可能不同的
                // 因此,在映射时也需要区分当前映射的是一方的数据还是多方的数据
                // 对于一方的数据,只需要映射一次
                // 当一方数据未映射时,其对象的值是null,而映射了之后则不为null
                // 判断当对象的值为null时说明尚未映射,则执行一方数据的映射
                if (dept == null) {
                    dept = new Dept();
                    dept.setId(rs.getInt("id"));
                    dept.setName(rs.getString("name"));
                    dept.setRemark(rs.getString("remark"));
                }

                // 对于多方数据,映射多次,每次映射生成一个对象
                Emp emp = new Emp();
                emp.setId(rs.getInt("e.id"));
                emp.setName(rs.getString("e.name"));
                emp.setSalary(rs.getDouble("salary"));
                // 最终添加到一方数据所映射的对象的集合属性中
                dept.addEmp(emp);
            }
        } catch (DataAccessException e) {
            e.printStackTrace();
            throw e;
        } catch (SQLException e) {
            e.printStackTrace();
            throw new DataAccessException(e);
        } finally {
            JDBCUtil.close(conn, ps, rs);
        }
        return dept;
    }

}
